/*
 * Copyright (c) 2006-2021, Ulandlink Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2021-09-05     lfz         first version
 */

#include "drv_gpio.h"
#include <rthw.h>
#include <pin.h>

#ifdef RT_USING_PIN

#define PIN_NUM(port, no) 				(((((port) & 0xFu) << 4) | ((no) & 0xFu)))

typedef struct pin_index
{
	int16_t 					index;
	rcu_periph_enum 			clk;
	uint32_t 					gpio_periph;
	uint32_t 					pin;
	uint8_t 					port_src;
	uint8_t 					pin_src;
}pin_index_t;

#define GD32_PIN(index, port, pin) \
{								   \
	index, 						   \
	RCU_GPIO##port,      		   \
	GPIO##port,					   \
	GPIO_PIN_##pin, 			   \
	EXTI_SOURCE_GPIO##port,        \
	EXTI_SOURCE_PIN##pin           \
}

#define GD32_PIN_DEFAULT            {-1, (rcu_periph_enum)0, 0, 0, 0, 0}

typedef struct pin_irq_map
{
	uint16_t pinbit;
	IRQn_Type irqno;
}pin_irq_map_t;

static const pin_index_t pins[] =
{
#if defined(GPIOA)
    GD32_PIN(0 ,  A, 0 ),
    GD32_PIN(1 ,  A, 1 ),
    GD32_PIN(2 ,  A, 2 ),
    GD32_PIN(3 ,  A, 3 ),
    GD32_PIN(4 ,  A, 4 ),
    GD32_PIN(5 ,  A, 5 ),
    GD32_PIN(6 ,  A, 6 ),
    GD32_PIN(7 ,  A, 7 ),
    GD32_PIN(8 ,  A, 8 ),
    GD32_PIN(9 ,  A, 9 ),
    GD32_PIN(10,  A, 10),
    GD32_PIN(11,  A, 11),
    GD32_PIN(12,  A, 12),
    GD32_PIN_DEFAULT,				//SWCLK
    GD32_PIN_DEFAULT,				//SWDIO
    GD32_PIN(15,  A, 15),
#if defined(GPIOB)
    GD32_PIN(16,  B, 0),
    GD32_PIN(17,  B, 1),
    GD32_PIN(18,  B, 2),
    GD32_PIN(19,  B, 3),
    GD32_PIN(20,  B, 4),
    GD32_PIN(21,  B, 5),
    GD32_PIN(22,  B, 6),
    GD32_PIN(23,  B, 7),
    GD32_PIN(24,  B, 8),
    GD32_PIN(25,  B, 9),
    GD32_PIN(26,  B, 10),
    GD32_PIN(27,  B, 11),
    GD32_PIN(28,  B, 12),
    GD32_PIN(29,  B, 13),
    GD32_PIN(30,  B, 14),
    GD32_PIN(31,  B, 15),
#if defined(GPIOC)
    GD32_PIN(32,  C, 0),
    GD32_PIN(33,  C, 1),
    GD32_PIN(34,  C, 2),
    GD32_PIN(35,  C, 3),
    GD32_PIN(36,  C, 4),
    GD32_PIN(37,  C, 5),
    GD32_PIN(38,  C, 6),
    GD32_PIN(39,  C, 7),
    GD32_PIN(40,  C, 8),
    GD32_PIN(41,  C, 9),
    GD32_PIN(42,  C, 10),
    GD32_PIN(43,  C, 11),
    GD32_PIN(44,  C, 12),
    GD32_PIN(45,  C, 13),
    GD32_PIN_DEFAULT,				//OSC32_IN
    GD32_PIN_DEFAULT,				//OSC32_OUT
#if defined(GPIOD)
    GD32_PIN(48,  D, 0),
    GD32_PIN(49,  D, 1),
    GD32_PIN(50,  D, 2),
    GD32_PIN(51,  D, 3),
    GD32_PIN(52,  D, 4),
    GD32_PIN(53,  D, 5),
    GD32_PIN(54,  D, 6),
    GD32_PIN(55,  D, 7),
    GD32_PIN(56,  D, 8),
    GD32_PIN(57,  D, 9),
    GD32_PIN(58,  D, 10),
    GD32_PIN(59,  D, 11),
    GD32_PIN(60,  D, 12),
    GD32_PIN(61,  D, 13),
    GD32_PIN(62,  D, 14),
    GD32_PIN(63,  D, 15),
#if defined(GPIOE)
    GD32_PIN(64,  E, 0),
    GD32_PIN(65,  E, 1),
    GD32_PIN(66,  E, 2),
    GD32_PIN(67,  E, 3),
    GD32_PIN(68,  E, 4),
    GD32_PIN(69,  E, 5),
    GD32_PIN(70,  E, 6),
    GD32_PIN(71,  E, 7),
    GD32_PIN(72,  E, 8),
    GD32_PIN(73,  E, 9),
    GD32_PIN(74,  E, 10),
    GD32_PIN(75,  E, 11),
    GD32_PIN(76,  E, 12),
    GD32_PIN(77,  E, 13),
    GD32_PIN(78,  E, 14),
    GD32_PIN(79,  E, 15),
#if defined(GPIOF)
    GD32_PIN(80,  F, 0),
    GD32_PIN(81,  F, 1),
    GD32_PIN(82,  F, 2),
    GD32_PIN(83,  F, 3),
    GD32_PIN(84,  F, 4),
    GD32_PIN(85,  F, 5),
    GD32_PIN(86,  F, 6),
    GD32_PIN(87,  F, 7),
    GD32_PIN(88,  F, 8),
    GD32_PIN(89,  F, 9),
    GD32_PIN(90,  F, 10),
    GD32_PIN(91,  F, 11),
    GD32_PIN(92,  F, 12),
    GD32_PIN(93,  F, 13),
    GD32_PIN(94,  F, 14),
    GD32_PIN(95,  F, 15),
#if defined(GPIOG)
    GD32_PIN(96,  G, 0),
    GD32_PIN(97,  G, 1),
    GD32_PIN(98,  G, 2),
    GD32_PIN(99,  G, 3),
    GD32_PIN(100,  G, 4),
    GD32_PIN(101,  G, 5),
    GD32_PIN(102,  G, 6),
    GD32_PIN(103,  G, 7),
    GD32_PIN(104,  G, 8),
    GD32_PIN(105,  G, 9),
    GD32_PIN(106,  G, 10),
    GD32_PIN(107,  G, 11),
    GD32_PIN(108,  G, 12),
    GD32_PIN(109,  G, 13),
    GD32_PIN(110,  G, 14),
    GD32_PIN(111,  G, 15),
#if defined(GPIOH)
    GD32_PIN(112,  H, 0),
    GD32_PIN(113,  H, 1),
    GD32_PIN(114,  H, 2),
    GD32_PIN(115,  H, 3),
    GD32_PIN(116,  H, 4),
    GD32_PIN(117,  H, 5),
    GD32_PIN(118,  H, 6),
    GD32_PIN(119,  H, 7),
    GD32_PIN(120,  H, 8),
    GD32_PIN(121,  H, 9),
    GD32_PIN(122,  H, 10),
    GD32_PIN(123,  H, 11),
    GD32_PIN(124,  H, 12),
    GD32_PIN(125,  H, 13),
    GD32_PIN(126,  H, 14),
    GD32_PIN(127,  H, 15),
#if defined(GPIOI)
    GD32_PIN(128,  I, 0),
    GD32_PIN(129,  I, 1),
    GD32_PIN(130,  I, 2),
    GD32_PIN(131,  I, 3),
    GD32_PIN(132,  I, 4),
    GD32_PIN(133,  I, 5),
    GD32_PIN(134,  I, 6),
    GD32_PIN(135,  I, 7),
    GD32_PIN(136,  I, 8),
    GD32_PIN(137,  I, 9),
    GD32_PIN(138,  I, 10),
    GD32_PIN(139,  I, 11),
    GD32_PIN_DEFAULT,
    GD32_PIN_DEFAULT,
    GD32_PIN_DEFAULT,
    GD32_PIN_DEFAULT,
#endif /* defined(GPIOI) */
#endif /* defined(GPIOH) */
#endif /* defined(GPIOG) */
#endif /* defined(GPIOF) */
#endif /* defined(GPIOE) */
#endif /* defined(GPIOD) */
#endif /* defined(GPIOC) */
#endif /* defined(GPIOB) */
#endif /* defined(GPIOA) */
};

static const pin_irq_map_t pin_irq_map[] =
{
	{GPIO_PIN_0,  EXTI0_IRQn},
	{GPIO_PIN_1,  EXTI1_IRQn},
	{GPIO_PIN_2,  EXTI2_IRQn},
	{GPIO_PIN_3,  EXTI3_IRQn},
	{GPIO_PIN_4,  EXTI4_IRQn},
	{GPIO_PIN_5,  EXTI5_9_IRQn},
	{GPIO_PIN_6,  EXTI5_9_IRQn},
	{GPIO_PIN_7,  EXTI5_9_IRQn},
	{GPIO_PIN_8,  EXTI5_9_IRQn},
	{GPIO_PIN_9,  EXTI5_9_IRQn},
	{GPIO_PIN_10, EXTI10_15_IRQn},
	{GPIO_PIN_11, EXTI10_15_IRQn},
	{GPIO_PIN_12, EXTI10_15_IRQn},
	{GPIO_PIN_13, EXTI10_15_IRQn},
	{GPIO_PIN_14, EXTI10_15_IRQn},
	{GPIO_PIN_15, EXTI10_15_IRQn},
};

struct rt_pin_irq_hdr pin_irq_hdr_tab[] =
{
	{-1, 0, RT_NULL, RT_NULL},
	{-1, 0, RT_NULL, RT_NULL},
	{-1, 0, RT_NULL, RT_NULL},
	{-1, 0, RT_NULL, RT_NULL},
	{-1, 0, RT_NULL, RT_NULL},
	{-1, 0, RT_NULL, RT_NULL},
	{-1, 0, RT_NULL, RT_NULL},
	{-1, 0, RT_NULL, RT_NULL},
	{-1, 0, RT_NULL, RT_NULL},
	{-1, 0, RT_NULL, RT_NULL},
	{-1, 0, RT_NULL, RT_NULL},
	{-1, 0, RT_NULL, RT_NULL},
	{-1, 0, RT_NULL, RT_NULL},
	{-1, 0, RT_NULL, RT_NULL},
	{-1, 0, RT_NULL, RT_NULL},
	{-1, 0, RT_NULL, RT_NULL},
};

#define ITEM_NUM(items) sizeof(items) / sizeof(items[0])
const struct pin_index *get_pin(rt_uint8_t pin)
{
	const struct pin_index *index;

	if (pin < ITEM_NUM(pins)) {
		index = &pins[pin];
		if (index->index == -1)
		index = RT_NULL;
	} else {
		index = RT_NULL;
	}

	return index;
};

void gd32_pin_mode(rt_device_t dev, rt_base_t pin, rt_base_t mode)
{
	const struct pin_index *index = RT_NULL;
	rt_uint32_t pin_mode = 0, pin_pupd = 0, pin_odpp = 0;

	index = get_pin(pin);
	if (index == RT_NULL){
		return;
	}

	/* GPIO Periph clock enable */
	rcu_periph_clock_enable(index->clk);
	pin_mode = GPIO_MODE_OUTPUT;

	switch(mode){
	case PIN_MODE_OUTPUT:
		/* output setting */
		pin_mode = GPIO_MODE_OUTPUT;
		pin_pupd = GPIO_PUPD_NONE;
		pin_odpp = GPIO_OTYPE_PP;
	break;
	case PIN_MODE_OUTPUT_OD:
		/* output setting: od. */
		pin_mode = GPIO_MODE_OUTPUT;
		pin_pupd = GPIO_PUPD_NONE;
		pin_odpp = GPIO_OTYPE_OD;
	break;
	case PIN_MODE_INPUT:
		/* input setting: not pull. */
		pin_mode = GPIO_MODE_INPUT;
		pin_pupd = GPIO_PUPD_NONE;
	break;
	case PIN_MODE_INPUT_PULLUP:
		/* input setting: pull up. */
		pin_mode = GPIO_MODE_INPUT;
		pin_pupd = GPIO_PUPD_PULLUP;
	break;
	case PIN_MODE_INPUT_PULLDOWN:
		/* input setting: pull down. */
		pin_mode = GPIO_MODE_INPUT;
		pin_pupd = GPIO_PUPD_PULLDOWN;
	break;
	default:
	break;
	}

	gpio_mode_set(index->gpio_periph, pin_mode, pin_pupd, index->pin);
	if (pin_mode == GPIO_MODE_OUTPUT){
		gpio_output_options_set(index->gpio_periph, pin_odpp, GPIO_OSPEED_50MHZ, index->pin);
	}
}

void gd32_pin_write(rt_device_t dev, rt_base_t pin, rt_base_t value)
{
	const struct pin_index *index = RT_NULL;

	index = get_pin(pin);
	if (index == RT_NULL){
		return;
	}

	gpio_bit_write(index->gpio_periph, index->pin, (bit_status)value);
}

int gd32_pin_read(rt_device_t dev, rt_base_t pin)
{
	int value = PIN_LOW;
	const struct pin_index *index = RT_NULL;

	index = get_pin(pin);
	if (index == RT_NULL){
		return value;
	}

	value = gpio_input_bit_get(index->gpio_periph, index->pin);
	return value;
}

rt_inline rt_int32_t bit2bitno(rt_uint32_t bit)
{
	rt_uint8_t i;
	for (i = 0; i < 32; i++){
		if ((0x01 << i) == bit) {
			return i;
		}
	}
	return -1;
}

rt_inline const pin_irq_map_t *get_pin_irq_map(rt_uint32_t pinbit)
{
	rt_int32_t map_index = bit2bitno(pinbit);
	if (map_index < 0 || map_index >= ITEM_NUM(pin_irq_map)) {
		return RT_NULL;
	}
	return &pin_irq_map[map_index];
};

rt_err_t gd32_pin_attach_irq(struct rt_device *device, rt_int32_t pin, rt_uint32_t mode, void (*hdr)(void *args), void *args)
{
	const struct pin_index *index = RT_NULL;
	rt_base_t level;
	rt_int32_t hdr_index = -1;

	index = get_pin(pin);
	if (index == RT_NULL){
		return RT_EINVAL;
	}

	hdr_index = bit2bitno(index->pin);
	if (hdr_index < 0 || hdr_index >= ITEM_NUM(pin_irq_map)){
		return RT_EINVAL;
	}

	level = rt_hw_interrupt_disable();
	if (pin_irq_hdr_tab[hdr_index].pin == pin &&
		pin_irq_hdr_tab[hdr_index].hdr == hdr &&
		pin_irq_hdr_tab[hdr_index].mode == mode &&
		pin_irq_hdr_tab[hdr_index].args == args){
		rt_hw_interrupt_enable(level);
		return RT_EOK;
	}
	if (pin_irq_hdr_tab[hdr_index].pin != -1){
		rt_hw_interrupt_enable(level);
		return RT_EFULL;
	}
	pin_irq_hdr_tab[hdr_index].pin = pin;
	pin_irq_hdr_tab[hdr_index].hdr = hdr;
	pin_irq_hdr_tab[hdr_index].mode = mode;
	pin_irq_hdr_tab[hdr_index].args = args;
	rt_hw_interrupt_enable(level);

	return RT_EOK;
}

rt_err_t gd32_pin_detach_irq(struct rt_device *device, rt_int32_t pin)
{
	const struct pin_index *index = RT_NULL;
	rt_base_t level;
	rt_int32_t hdr_index = -1;

	index = get_pin(pin);
	if (index == RT_NULL) {
		return RT_EINVAL;
	}

	hdr_index = bit2bitno(index->pin);
	if (hdr_index < 0 || hdr_index >= ITEM_NUM(pin_irq_map)){
		return RT_EINVAL;
	}

	level = rt_hw_interrupt_disable();
	if (pin_irq_hdr_tab[hdr_index].pin == -1){
		rt_hw_interrupt_enable(level);
		return RT_EOK;
	}
	pin_irq_hdr_tab[hdr_index].pin = -1;
	pin_irq_hdr_tab[hdr_index].hdr = RT_NULL;
	pin_irq_hdr_tab[hdr_index].mode = 0;
	pin_irq_hdr_tab[hdr_index].args = RT_NULL;
	rt_hw_interrupt_enable(level);

	return RT_EOK;
}

rt_err_t gd32_pin_irq_enable(struct rt_device *device, rt_base_t pin, rt_uint32_t enabled)
{
	const struct pin_index *index;
	const pin_irq_map_t *irqmap;
	rt_base_t level;
	rt_int32_t hdr_index = -1;
	exti_trig_type_enum trigger_mode;

	index = get_pin(pin);
	if (index == RT_NULL){
		return RT_EINVAL;
	}

	if (enabled == PIN_IRQ_ENABLE){
		hdr_index = bit2bitno(index->pin);
		if (hdr_index < 0 || hdr_index >= ITEM_NUM(pin_irq_map)){
			return RT_EINVAL;
		}

		level = rt_hw_interrupt_disable();
		if (pin_irq_hdr_tab[hdr_index].pin == -1){
			rt_hw_interrupt_enable(level);
			return RT_EINVAL;
		}

		irqmap = &pin_irq_map[hdr_index];

		switch (pin_irq_hdr_tab[hdr_index].mode) {
		case PIN_IRQ_MODE_RISING:
			trigger_mode = EXTI_TRIG_RISING;
		break;
		case PIN_IRQ_MODE_FALLING:
			trigger_mode = EXTI_TRIG_FALLING;
		break;
		case PIN_IRQ_MODE_RISING_FALLING:
			trigger_mode = EXTI_TRIG_BOTH;
		break;
		default:
			rt_hw_interrupt_enable(level);
		return RT_EINVAL;
		}

		rcu_periph_clock_enable(RCU_SYSCFG);

		/* enable and set interrupt priority */
		nvic_irq_enable(irqmap->irqno, 5U, 0U);

		/* connect EXTI line to  GPIO pin */
		syscfg_exti_line_config(index->port_src, index->pin_src);

		/* configure EXTI line */
		exti_init((exti_line_enum)(index->pin), EXTI_INTERRUPT, trigger_mode);
		exti_interrupt_flag_clear((exti_line_enum)(index->pin));

		rt_hw_interrupt_enable(level);
	} else if (enabled == PIN_IRQ_DISABLE){
		irqmap = get_pin_irq_map(index->pin);
		if (irqmap == RT_NULL){
			return RT_EINVAL;
		}
		nvic_irq_disable(irqmap->irqno);
	} else {
		return RT_EINVAL;
	}

	return RT_EOK;
}

static rt_base_t gd32_pin_get(const char *name)
{
	rt_base_t pin = 0;
	int32_t hw_port_num, hw_pin_num = 0;
	int16_t i, name_len;

	/* PB2, PB12*/
	name_len = rt_strlen(name);

	if ((name_len < 3) || (name_len >= 5)){
		return -RT_EINVAL;
	}
	if ((name[0] != 'P')){
		return -RT_EINVAL;
	}

	if ((name[1] >= 'A') && (name[1] <= 'Z')){
		hw_port_num = (int32_t)(name[1] - 'A');
	} else {
		return -RT_EINVAL;
	}

	for (i = 2; i < name_len; i++){
		hw_pin_num *= 10;
		hw_pin_num += name[i] - '0';
	}

	pin = PIN_NUM(hw_port_num, hw_pin_num);

	return pin;
}

const static struct rt_pin_ops gd32_pin_ops =
{
	gd32_pin_mode,
	gd32_pin_write,
	gd32_pin_read,
	gd32_pin_attach_irq,
	gd32_pin_detach_irq,
	gd32_pin_irq_enable,
	gd32_pin_get,
};

rt_inline void pin_irq_hdr(int irqno)
{
	if (pin_irq_hdr_tab[irqno].hdr) {
		pin_irq_hdr_tab[irqno].hdr(pin_irq_hdr_tab[irqno].args);
	}
}

void GD32_GPIO_EXTI_IRQHandler(rt_int8_t exti_line)
{
	if (RESET != exti_interrupt_flag_get((exti_line_enum)(1 << exti_line))){
		pin_irq_hdr(exti_line);
		exti_interrupt_flag_clear((exti_line_enum)(1 << exti_line));
	}
}

void EXTI0_IRQHandler(void)
{
	rt_interrupt_enter();
	GD32_GPIO_EXTI_IRQHandler(0);
	rt_interrupt_leave();
}

void EXTI1_IRQHandler(void)
{
	rt_interrupt_enter();
	GD32_GPIO_EXTI_IRQHandler(1);
	rt_interrupt_leave();
}

void EXTI2_IRQHandler(void)
{
	rt_interrupt_enter();
	GD32_GPIO_EXTI_IRQHandler(2);
	rt_interrupt_leave();
}

void EXTI3_IRQHandler(void)
{
	rt_interrupt_enter();
	GD32_GPIO_EXTI_IRQHandler(3);
	rt_interrupt_leave();
}

void EXTI4_IRQHandler(void)
{
	rt_interrupt_enter();
	GD32_GPIO_EXTI_IRQHandler(4);
	rt_interrupt_leave();
}

void EXTI5_9_IRQHandler(void)
{
	rt_interrupt_enter();
	GD32_GPIO_EXTI_IRQHandler(5);
	GD32_GPIO_EXTI_IRQHandler(6);
	GD32_GPIO_EXTI_IRQHandler(7);
	GD32_GPIO_EXTI_IRQHandler(8);
	GD32_GPIO_EXTI_IRQHandler(9);
	rt_interrupt_leave();
}

void EXTI10_15_IRQHandler(void)
{
	rt_interrupt_enter();
	GD32_GPIO_EXTI_IRQHandler(10);
	GD32_GPIO_EXTI_IRQHandler(11);
	GD32_GPIO_EXTI_IRQHandler(12);
	GD32_GPIO_EXTI_IRQHandler(13);
	GD32_GPIO_EXTI_IRQHandler(14);
	GD32_GPIO_EXTI_IRQHandler(15);
	rt_interrupt_leave();
}

int32_t rt_hw_pin_init(void)
{
	return (rt_device_pin_register("pin", &gd32_pin_ops, RT_NULL));
}
INIT_BOARD_EXPORT(rt_hw_pin_init);
#endif /* RT_USING_PIN */
